﻿#include <xy/render/painter.h>
#include <xy/utils/scope_profiler.h>

namespace xy
{

namespace render
{

using namespace xy::math;
using namespace xy::geom;
using namespace xy::utils;

painter::painter(int width, int height, int msaaLevel)
    : frame(width, height, 4), zBuffer(width * msaaLevel, height * msaaLevel, 4), msaaLevel(msaaLevel)
{
}

void painter::resize(int width, int height)
{
    frame.resize(width, height);
    zBuffer.resize(width * msaaLevel, height * msaaLevel);
}

void painter::clear_color(const color_t &color)
{
    for (int y = 0; y < frame->height; y++)
        for (int x = 0; x < frame->width; x++)
            frame.set(x, y, color);
}

void painter::clear_depth(int depth)
{
    for (int y = 0; y < zBuffer->height; y++)
        for (int x = 0; x < zBuffer->width; x++)
            zBuffer.set(x, y, depth);
}

void painter::draw_circle(int x, int y, int r, const color_t &color)
{
    // 绘制圆上的点
    auto circlePoint = [&](int xc, int yc, int x, int y) {
        // 由于圆分成 8 个部分，利用对称性同时绘制 8 个点
        frame.safe_set(xc + x, yc + y, color);
        frame.safe_set(xc - x, yc + y, color);
        frame.safe_set(xc + x, yc - y, color);
        frame.safe_set(xc - x, yc - y, color);
        frame.safe_set(xc + y, yc + x, color);
        frame.safe_set(xc - y, yc + x, color);
        frame.safe_set(xc + y, yc - x, color);
        frame.safe_set(xc - y, yc - x, color);
    };

    // 距离判断条件
    int Dd = 2 * (1 - r);
    int dHD, dDV;
    int x0 = 0, y0 = r;

    // 下一个选择的点
    while (y0 >= x0)
    {
        // 绘制当前点
        circlePoint(x, y, x0, y0);

        // 根据 D 在圆内外分情况讨论，选择下一个点
        char nextPoint = 'D';
        if (Dd < 0)
        {
            // 选择 H 或 D，dHD = Dh + Dd
            dHD = 2 * (Dd + y0) - 1;
            nextPoint = (dHD <= 0) ? 'H' : 'D';
        }
        else if (Dd > 0)
        {
            // 选择 V 或 D，dDV = Dd + Dv
            dDV = 2 * (Dd - x0) - 1;
            nextPoint = (dDV <= 0) ? 'D' : 'V';
        }

        switch (nextPoint)
        {
        case 'H':
            // H = (x+1,y)
            x0++;
            Dd += 2 * x0 + 1;
            break;
        case 'D':
            // D = (x+1,y-1)
            x0++, y0--;
            Dd += 2 * (x0 - y0 + 1);
            break;
        case 'V':
            // V = (x,y-1)
            y0--;
            Dd += (-2 * y0 + 1);
            break;
        }
    }
}

void painter::draw_circle_pointwise(int x, int y, int r, const color_t &color)
{
    // 绘制圆上的点
    auto circlePoint = [&](int xc, int yc, int x, int y) {
        // 由于圆分成 8 个部分，利用对称性同时绘制 8 个点
        frame.safe_set(xc + x, yc + y, color);
        frame.safe_set(xc - x, yc + y, color);
        frame.safe_set(xc + x, yc - y, color);
        frame.safe_set(xc - x, yc - y, color);
        frame.safe_set(xc + y, yc + x, color);
        frame.safe_set(xc - y, yc + x, color);
        frame.safe_set(xc + y, yc - x, color);
        frame.safe_set(xc - y, yc - x, color);
    };

    int x0 = 0, y0 = r;
    int F = 0;

    // 绘制 1/8 圆
    while (y0 >= -x0)
    {
        circlePoint(x, y, x0, y0);
        if (F > 0)
        {
            F += -2 * y0 + 1;
            y0--;
        }
        else
        {
            F += -2 * x0 + 1;
            x0--;
        }
    }
}

void painter::draw_circle_bresenham(int x, int y, int r, const color_t &color)
{
    // 绘制圆上的点
    auto circlePoint = [&](int xc, int yc, int x, int y) {
        // 由于圆分成 8 个部分，利用对称性同时绘制 8 个点
        frame.safe_set(xc + x, yc + y, color);
        frame.safe_set(xc - x, yc + y, color);
        frame.safe_set(xc + x, yc - y, color);
        frame.safe_set(xc - x, yc - y, color);
        frame.safe_set(xc + y, yc + x, color);
        frame.safe_set(xc - y, yc + x, color);
        frame.safe_set(xc + y, yc - x, color);
        frame.safe_set(xc - y, yc - x, color);
    };

    // 从 (0,r) 位置出发
    int x0 = 0, y0 = r;
    int d = 3 - 2 * r;
    circlePoint(x, y, x0, y0);

    // 只绘制 1/8 圆
    while (y0 >= x0)
    {
        if (d > 0)
        {
            y0--;
            d += 4 * (x0 - y0) + 10;
        }
        else
            d += +4 * x0 + 6;
        x0++;
        circlePoint(x, y, x0, y0);
    }
}

void painter::draw_ellispe(int x, int y, int width, int height, const color_t &color)
{
    // 绘制椭圆上的点
    auto ellipsePoint = [&](int xc, int yc, int x, int y) {
        // 由于椭圆分成 4 个部分，利用对称性同时绘制 4 个点
        frame.safe_set(xc + x, yc + y, color);
        frame.safe_set(xc - x, yc + y, color);
        frame.safe_set(xc + x, yc - y, color);
        frame.safe_set(xc - x, yc - y, color);
    };

    // 椭圆两个半长轴
    float rx = width / 2.0f;
    float ry = height / 2.0f;

    // 判断哪个方向是主要延伸方向
    bool steep = false;
    if (rx < ry)
    {
        std::swap(x, y);
        std::swap(rx, ry);
        steep = true;
    }

    // 绘制直到斜率为 -1 的圆弧
    auto arc = [&](int x, int y, float rx, float ry) {
        int xc = x + rx;
        int yc = y + ry;
        int x0 = 0, y0 = ry;

        // 初始判断条件 (1, ry + 0.5)
        float test = ry * ry + rx * rx * ry + 0.25 * rx * rx;

        while (y0 >= 0)
        {
            // 绘制当前点共对称的四个点
            if (steep)
                ellipsePoint(yc, xc, y0, x0);
            else
                ellipsePoint(xc, yc, x0, y0);

            // 判断条件
            if (test < 0)
            {
                // 增量计算下一步
                test += ry * ry * (1.0f + 2.0f * x0);

                // 移动到右边一点或右下点
                x0 += 1;
            }
            else
            {
                // 增量计算下一步
                test += ry * ry * (1.0f + 2.0f * x0) + rx * rx * (1.0f - 2.0f * y0);

                // 移动到右下角点
                x0 += 1;
                y0 -= 1;
            }

            // 当斜率小于 -1 时退出
            if (ry * ry * x0 > rx * rx * y0)
                break;
        }
    };

    // 沿着长轴方向绘制，直到斜率小于 -1
    arc(x, y, rx, ry);

    // 交换 x,y
    std::swap(x, y);
    std::swap(rx, ry);
    steep = !steep;

    // 绘制剩余部分
    arc(x, y, rx, ry);
}

void painter::fill_circle(int x, int y, int r, const color_t &color)
{
    // 绘制圆上的点
    auto circlePoint = [&](int xc, int yc, int x, int y) {
        // 由于圆分成 8 个部分，利用对称性同时绘制 8 个点
        fill_boundary_4(xc + x, yc + y, color, color);
        fill_boundary_4(xc - x, yc + y, color, color);
        fill_boundary_4(xc + x, yc - y, color, color);
        fill_boundary_4(xc - x, yc - y, color, color);
        fill_boundary_4(xc + y, yc + x, color, color);
        fill_boundary_4(xc - y, yc + x, color, color);
        fill_boundary_4(xc + y, yc - x, color, color);
        fill_boundary_4(xc - y, yc - x, color, color);
    };

    // 从 (0,r) 位置出发
    int x0 = 0, y0 = r;
    int d = 3 - 2 * r;
    circlePoint(x, y, x0, y0);

    // 只绘制 1/8 圆
    while (y0 >= x0)
    {
        if (d > 0)
        {
            y0--;
            d += 4 * (x0 - y0) + 10;
        }
        else
            d += +4 * x0 + 6;
        x0++;
        circlePoint(x, y, x0, y0);
    }
}

void painter::fill_boundary_4(int x, int y, const color_t &fillColor, const color_t &boundaryColor)
{
    if (x < 0 || x >= frame->width || y < 0 || y >= frame->height)
        return;

    if (frame.get(x, y) != boundaryColor && frame.get(x, y) != fillColor)
    {
        frame.set(x, y, fillColor);
        fill_boundary_4(x + 1, y, fillColor, boundaryColor);
        fill_boundary_4(x, y + 1, fillColor, boundaryColor);
        fill_boundary_4(x - 1, y, fillColor, boundaryColor);
        fill_boundary_4(x, y - 1, fillColor, boundaryColor);
    }
}

void painter::fill_flood_4(int x, int y, const color_t &fillColor, const color_t &sameColor)
{
    if (x < 0 || x >= frame->width || y < 0 || y >= frame->height)
        return;

    if (frame.get(x, y) == sameColor)
    {
        frame.set(x, y, fillColor);
        fill_flood_4(x + 1, y, fillColor, sameColor);
        fill_flood_4(x, y + 1, fillColor, sameColor);
        fill_flood_4(x - 1, y, fillColor, sameColor);
        fill_flood_4(x, y - 1, fillColor, sameColor);
    }
}

void painter::write_image(std::string filename)
{
    frame.flip_vertically();
    frame.write(filename);
    frame.flip_vertically();

    zBuffer.flip_vertically();
    zBuffer.write(filename + ".zbuffer.png");
    zBuffer.flip_vertically();
}

void painter::draw_point(const float *v, int size, std::function<bool(std::uint32_t, std::uint32_t)> depthFunc,
                         const color_t &color)
{
    int x = std::round(v[0]), y = std::round(v[1]);
    std::uint32_t z = depth_mapping(v[2], camera::zNear, camera::zFar);
    if (depthFunc(z, zBuffer.safe_get(x * msaaLevel, y * msaaLevel, std::uint32_t{})))
    {
        if (size < 2)
            frame.safe_set(x, y, color);
        else
        {
            draw_circle_bresenham(x, y, size / 2, color);
            fill_circle(x, y, size / 2 - 1, color);
        }
    }
}

void painter::draw_line(const float *v0, const float *v1, std::function<bool(std::uint32_t, std::uint32_t)> depthFunc,
                        std::function<color_t(float)> colorFunc)
{
    int x0 = std::round(v0[0]), y0 = std::round(v0[1]);
    int x1 = std::round(v1[0]), y1 = std::round(v1[1]);
    float z0 = v0[2], z1 = v1[2];
    float w0 = v0[3], w1 = v1[3];

    // 判断哪个方向是主要延伸方向
    bool steep = false;
    if (std::abs(x0 - x1) < std::abs(y0 - y1))
    {
        std::swap(x0, y0);
        std::swap(x1, y1);
        steep = true;
    }

    // 确保主方向坐标递增
    bool reverse = false;
    if (x0 > x1)
    {
        std::swap(x0, x1);
        std::swap(y0, y1);
        std::swap(z0, z1);
        std::swap(w0, w1);
        reverse = true;
    }

    int dx = x1 - x0;
    int dy = y1 - y0;

    // 利用 *2 规避中点比较时的 0.5 浮点数
    int derror2 = std::abs(dy) * 2;
    int error2 = 0;
    int y = y0;

    auto blend = [](const color_t &fg, const color_t &bg, float alpha) -> color_t {
        return color_t(fg.bgra[2] * alpha + bg.bgra[2] * (1 - alpha), fg.bgra[1] * alpha + bg.bgra[1] * (1 - alpha),
                       fg.bgra[0] * alpha + bg.bgra[0] * (1 - alpha), fg.bgra[3] * alpha);
    };

    for (int x = x0; x <= x1; x++)
    {
        // 注意重心坐标，当 x = x0 时 alpha = 1，和一般的线性插值相反
        float alpha = (x1 - x0) == 0.0f ? 1.0f : (float)(x1 - x) / (x1 - x0);
        float beta = 1.0f - alpha;

        // 透视校正
        alpha *= w0, beta *= w1;
        float w = 1.0f / (alpha + beta);
        alpha *= w, beta *= w;

        std::uint32_t z = depth_mapping(alpha * z0 + beta * z1, camera::zNear, camera::zFar);
        if (steep)
        {
            if (depthFunc(z, zBuffer.safe_get(y * msaaLevel, x * msaaLevel, std::uint32_t{})))
            {
                if (reverse)
                    std::swap(alpha, beta);

                frame.safe_set(y, x, colorFunc(beta));
            }
        }
        else
        {
            if (depthFunc(z, zBuffer.safe_get(x * msaaLevel, y * msaaLevel, std::uint32_t{})))
            {
                if (reverse)
                    std::swap(alpha, beta);

                frame.safe_set(x, y, colorFunc(beta));
            }
        }

        error2 += derror2;
        if (error2 > dx)
        {
            y += (y1 > y0 ? 1 : -1);
            error2 -= dx * 2;
        }
    }
}

void painter::draw_line_antialias(const float *v0, const float *v1, int width,
                                  std::function<bool(std::uint32_t, std::uint32_t)> depthFunc,
                                  std::function<color_t(float)> colorFunc)
{
    int x0 = std::round(v0[0]), y0 = std::round(v0[1]);
    int x1 = std::round(v1[0]), y1 = std::round(v1[1]);
    float z0 = v0[2], z1 = v1[2];
    float w0 = v0[3], w1 = v1[3];

    // 判断哪个方向是主要延伸方向
    bool steep = false;
    if (std::abs(x0 - x1) < std::abs(y0 - y1))
    {
        std::swap(x0, y0);
        std::swap(x1, y1);
        steep = true;
    }

    // 确保主方向坐标递增
    bool reverse = false;
    if (x0 > x1)
    {
        std::swap(x0, x1);
        std::swap(y0, y1);
        std::swap(z0, z1);
        std::swap(w0, w1);
        reverse = true;
    }

    float dx = x1 - x0;
    float dy = y1 - y0;
    float gradient = (dx < std::numeric_limits<float>::epsilon()) ? 1.0f : dy / dx;
    float intersectY = y0;

    // 整数部分、小数部分、1-小数部分
    auto iPart = [](float x) -> int { return int(x); };
    auto fPart = [](float x) -> float { return x - int(x); };
    auto rPart = [](float x) -> float { return 1.0f - (x - int(x)); };

    auto blend = [](const color_t &fg, const color_t &bg, float alpha) -> color_t {
        return color_t(fg.bgra[2] * alpha + bg.bgra[2] * (1 - alpha), fg.bgra[1] * alpha + bg.bgra[1] * (1 - alpha),
                       fg.bgra[0] * alpha + bg.bgra[0] * (1 - alpha), fg.bgra[3] * alpha);
    };

    for (int x = x0; x <= x1; x++)
    {
        // 注意重心坐标，当 x = x0 时 alpha = 1，和一般的线性插值相反
        float alpha = (x1 - x0) == 0.0f ? 1.0f : (float)(x1 - x) / (x1 - x0);
        float beta = 1.0f - alpha;

        // 透视校正
        alpha *= w0, beta *= w1;
        float w = 1.0f / (alpha + beta);
        alpha *= w, beta *= w;

        std::uint32_t z = depth_mapping(alpha * z0 + beta * z1, camera::zNear, camera::zFar);
        if (steep)
        {
            if (depthFunc(z, zBuffer.safe_get(iPart(intersectY) * msaaLevel, x * msaaLevel, std::uint32_t{})))
            {
                if (reverse)
                    std::swap(alpha, beta);

                // 这里要使用一般的线性插值
                color_t color = colorFunc(beta);
                for (int i = 0; i < width * 2; i++)
                    frame.safe_set(iPart(intersectY) - width + i, x, color);

                auto bg0 = frame.safe_get(iPart(intersectY) + width, x, std::uint32_t{0xff000000});
                frame.safe_set(iPart(intersectY) + width, x, color_t::alpha(blend(color, bg0, fPart(intersectY)), bg0));

                auto bg1 = frame.safe_get(iPart(intersectY) - width - 1, x, std::uint32_t{0xff000000});
                frame.safe_set(iPart(intersectY) - width - 1, x,
                               color_t::alpha(blend(color, bg1, rPart(intersectY)), bg1));
            }
        }
        else
        {
            if (depthFunc(z, zBuffer.safe_get(x * msaaLevel, iPart(intersectY) * msaaLevel, std::uint32_t{})))
            {
                if (reverse)
                    std::swap(alpha, beta);

                // 这里要使用一般的线性插值
                color_t color = colorFunc(beta);
                for (int i = 0; i < width * 2; i++)
                    frame.safe_set(x, iPart(intersectY) - width + i, color);

                auto bg0 = frame.safe_get(x, iPart(intersectY) + width, std::uint32_t{0xff000000});
                frame.safe_set(x, iPart(intersectY) + width, color_t::alpha(blend(color, bg0, fPart(intersectY)), bg0));

                auto bg1 = frame.safe_get(x, iPart(intersectY) - width - 1, std::uint32_t{0xff000000});
                frame.safe_set(x, iPart(intersectY) - width - 1,
                               color_t::alpha(blend(color, bg1, rPart(intersectY)), bg1));
            }
        }
        intersectY += gradient;
    }
}

void painter::fill_triangle(const float *v0, const float *v1, const float *v2,
                            std::function<bool(std::uint32_t, std::uint32_t)> depthFunc,
                            std::function<color_t(float, float, float)> colorFunc)
{
    // 计算包围盒
    vec2f rx{std::min(v0[0], std::min(v1[0], v2[0])), std::max(v0[0], std::max(v1[0], v2[0]))};
    vec2f ry{std::min(v0[1], std::min(v1[1], v2[1])), std::max(v0[1], std::max(v1[1], v2[1]))};

    // 三角形顶点
    std::vector<vec2f> pts({vec2f{v0[0], v0[1]}, vec2f{v1[0], v1[1]}, vec2f{v2[0], v2[1]}});
    int rxc = std::ceil(rx[1]), ryc = std::ceil(ry[1]);

#pragma omp parallel for collapse(2)
    for (int x = rx[0]; x <= rxc; x++)
    {
        for (int y = ry[0]; y <= ryc; y++)
        {
            if (x < 0 || x >= frame->width || y < 0 || y >= frame->height)
                continue;

            // 使用像素中心点
            if (auto option = barycentric(vec2f{x + 0.5f, y + 0.5f}, pts.data()))
            {
                // 如果重心坐标有一个负值，则它在外侧
                auto coords = option.value();
                if (coords[0] < 0 || coords[1] < 0 || coords[2] < 0)
                    continue;

                // 透视校正，注意 screen_mapping 已经将 w 取倒数
                coords = perspective_correct(coords, v0[3], v1[3], v2[3]);

                // 提前深度测试（注意 0.9f 为了防止浮点乘法溢出）
                std::uint32_t z = depth_mapping(coords[0] * v0[2] + coords[1] * v1[2] + coords[2] * v2[2],
                                                camera::zNear, camera::zFar);

                // msaa 采样
                if (msaaLevel > 1)
                {
                    int count = 0;
                    for (int i = 0; i < msaaLevel; i++)
                    {
                        for (int j = 0; j < msaaLevel; j++)
                        {
                            if (auto option =
                                    barycentric(vec2f{x + msaa::samples[msaaLevel][i], y + msaa::samples[msaaLevel][j]},
                                                pts.data()))
                            {
                                // 提供基础偏移值，防止遗漏边界附近的采样点
                                auto coords = option.value();
                                if (coords[0] < 0 || coords[1] < 0 || coords[2] < 0)
                                    continue;

                                if (depthFunc(z, zBuffer.get(x * msaaLevel + i, y * msaaLevel + j)))
                                {
                                    zBuffer.set(x * msaaLevel + i, y * msaaLevel + j, z);
                                    count++;
                                }
                            }
                        }
                    }

                    // 注意排除掉被完全遮挡的像素
                    if (count > 0)
                    {
                        float t = (float)count / msaaLevel / msaaLevel;
                        frame.set(x, y,
                                  color_t::alpha(colorFunc(coords[0], coords[1], coords[2]) * t, frame.get(x, y)));
                    }
                }
                else
                {
                    if (depthFunc(z, zBuffer.get(x, y)))
                    {
                        frame.set(x, y, color_t::alpha(colorFunc(coords[0], coords[1], coords[2]), frame.get(x, y)));
                        zBuffer.set(x, y, z);
                    }
                }
            }
        }
    }
}

} // namespace render

} // namespace xy
